Creación: 18-10-2008
1.
Introducción
2. Entorno
3.
El código que queremos probar: Autentia.java
4.
El test: AutentiaTest.java
5.
Configurando el pom.xml de Maven
6.
Nuestro primer informe
7.
Mejorando nuestro test, y viendo como funciona Cobertura
8.
Forzar un mínimo de cobertura
9.
Mejorando nuestro test (segunda parte)
10.
Conclusiones
11.
Sobre el autor
Cobertura (http://cobertura.sourceforge.net/) es una herramienta libre (GPL) escrita en Java, que nos permite comprobar el porcentaje de código al que accedemos desde los test. Es decir, Conbertura nos permite saber cuanto código estamos realmente probando con nuestros test.
De esta forma Cobertura se convierte en una potente herramienta de trabajo, ya que lo podemos usar como medida de calidad (mientras más código tengamos probado, más garantías tenemos de que podemos hacer refactorizaciones sin peligro).
Además Cobertura también nos indica la complejidad ciclomática de McCabe (http://en.wikipedia.org/wiki/Cyclomatic_complexity). Esto nos dice como de "complejo" es un método. Esto nos puede servir para orientar nuestros test y probar primero las piezas más complejas, o incluso nos puede hacer plantearnos una refactorización para bajar la complejidad del código.
Como veis, muchas son las ventajas de usar herramientas de este estilo, así que en este tutorial vamos a ver como integrar Cobertura en nuestro ciclo de Maven 2 (http://maven.apache.org/), y aprenderemos a interpretar los sencillísimos informes que nos va a generar (son tan sencillos de interpretar, que se convierten en otro de los puntos fuertes de Cobertura).
El tutorial está escrito usando el siguiente entorno:
Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).
Nvidia GEFORCE GO 7700
Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.26, KDE 3.5
Java Sun 1.6.0_07
Maven 2.0.9
Cobertura 1.9
Vamos a poner un sencillo ejemplo, una sola clase con un sólo método:
package com.autentia.tutorial; public class Autentia { public void tellMeSomething(int i) { if (i < 5) { System.out.println("Soy menor que cinco"); return; } if (i % 2 == 0) { System.out.println("Soy un número par"); return; } } }
Un pequeño test para probar un pequeño programa ;)
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); } }
En el pom.xml
añadiremos las siguientes
líneas para que se nos generen los informes de Cobertura:
... <reporting> <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> </plugin> </plugins> ... </reporting> ...
Los informes de Cobertura se generan dentro del "site" de Maven, así que tendremos que ejecutar:
$ mvn site
Si ahora nos vamos a la sección de informes veremos que tenemos uno llamado: "Cobertura Test Coverage". Le pinchamos y deberíamos ver algo similar a esto:
El informe es como si se tratara de una documentación de Javadoc, donde arriba a la izquierda tenemos los paquetes, justo debajo las clases (o todas, o las del paquete seleccionado), y a la derecha tenemos la información del elemento seleccionado (general, paquete, clase, ...).
En la imagen se pude ver como el paquete com.autentia.tutorial
sólo está probado en un 12%.
Si pinchamos sobre la clase, veremos algo como:
En la zona de la derecha, arriba podemos ver una pequeña tabla resumen:
Classes in this File: clases en este fichero, por si tenemos clases internas, que no es el caso.
Line Coverage: Cuantas líneas de este fichero se han probado. Nos dice que se ha probado 1 línea de 8 líneas ejecutables (para el Cobertura, las líneas ejecutables con la 3, 6, 7, 8, 11, 12, 13, 15, que son las que están marcadas en verde en la primera columna).
Branch Coverage: Nos indica cuanto posibles caminos (if else, while, for, ...) se han probado. En el informe vemos que se han probado 0 caminos de 4 posibles.
Complexity: La complejidad ciclomática McCabe de este fichero.
Justo debajo de esta tabla resumen no encontramos con el código fuente:
La columna de la izquierda es el número de línea (en verde si se trata de una línea ejecutable), la siguiente columna es el número de veces que la línea ha sido probada (las veces que se ha ejecutado esa línea). En la imagen podemos ver como por la línea 3 (la construcción de la clase) sólo se ha pasado 1 vez.
Por último podemos ver como en rojo tenemos marcadas todas las líneas que no han sido probadas.
La conclusión es que nuestro test no es demasiado bueno :( pero gracias Cobertura nos hemos dado cuenta de ello, y por eso podemos mejorar :)
Antes de hacer nada, vamos a explicar un poco como funciona
Cobertura. Cobertura lo que hace es instrumentalizar nuestro código.
Esto significa que, en tiempo de compilación lo "altera"
para llevar un contador del número de veces que se pasa por cada
línea. Esta información se va acumulando en un fichero llamado
cobertura.ser
.
Y ojo porque hemos dicho "acumulando", esto quiere decir que siempre se añade nunca se borra. Esto es necesario para poder probar, por ejemplo, distintos módulos o distintos conjuntos de pruebas, pero esto implica que si ejecutamos dos veces el mismo test, el número de líneas ejecutadas se irá incrementando. Para evitar esto tenemos dos soluciones:
$ mvn clean
: borra toda la información de
salida de Maven (un poco drástico)
$ mvn cobertura
: clean borra sólo la
información que almacena Cobertura, con lo que la siguiente vez que
generemos los informes estos se harán a partir de un entorno
"limpio".
NOTA: No debemos preocuparnos por la instrumentalización del código, ya que Cobertura la hace en un directorio diferente, por lo que nuestras clases, que luego se empaquetarán, quedan intactas.
Ahora nuestro test va a ser el siguiente:
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); autentia.tellMeSomething(3); } }
Para lanzar otra vez el site, haremos:
$ mvn cobertura:clean site
Esta vez el resultado del informe será:
Se puede ver como hemos mejorado bastante: ya estamos probando el 50% del código, aunque todavía sólo un 25% de los caminos posibles.
Hasta ahora sólo hemos estado usando Cobertura para sacar informes. Pero también podemos integrarlo en el ciclo de desarrollo, de forma que si nuestro código no está mínimamente cubierto por los test, no nos deje sacar una versión.
Para conseguir esto, en nuestro pom.xml
, además de
la configuración anterior, añadiremos las siguientes líneas:
... <build> <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <configuration> <check> <haltOnFailure>true</haltOnFailure> </check> </configuration> <executions> <execution> <goals> <goal>clean</goal> <goal>check</goal> </goals> </execution> </executions> </plugin> ... </plugins> </build> ...
Ojo, porque estas líneas las hemos puesto dentro del elemento build
,
mientras que antes las habíamos puesto dentro del elemento
reporting
.
Por defecto Cobertura va a exigir un mínimo de un 50% de líneas probadas y un mínimo de un 50% de caminos probados. Esto es configurable y podemos aumentar o disminuir el porcentaje. Incluso podemos configurarlo por paquete o incluso para una clase concreta. Para saber como hacer esto, podéis encontrar información en la documentación del plugin: http://mojo.codehaus.org/cobertura-maven-plugin/usage.html
Si ahora ejecutamos:
$ mvn clean install
Veremos el siguiente error:
Cobertura: Loaded information on 1 classes. Cobertura: Saved information on 1 classes. [INFO] [cobertura:check {execution: default}] [INFO] Cobertura 1.9 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file Cobertura: Loaded information on 1 classes. [ERROR] com.autentia.tutorial.Autentia failed check. Branch coverage rate of 25.0% is below 50.0% Package com.autentia.tutorial failed check. Package branch coverage rate of 25.0% is below 50.0% Project failed check. Total branch coverage rate of 25.0% is below 50.0%
Hasta que no tengamos suficientemente cubierto nuestro código, Maven no nos dejará continuar.
Añadimos una nueva línea a nuestro test:
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); autentia.tellMeSomething(3); autentia.tellMeSomething(6); } }
Ahora si intentamos compilar no tendremos ningún problema. Y si generamos los informes obtendremos esto:
Se ve como estamos probando el 88% de las líneas y el 75% de los caminos posibles.
No hay que buscar la calidad perfecta ni el 100% de cobertura, esto no es inteligente ni práctico, ya que nos llevaría demasiado tiempo y esfuerzo. Pero si es necesario unos mínimos de calidad y enfocar nuestros esfuerzos a probar las piezas más complicadas o más importantes para negocio.
Herramientas como Cobertura nos ayudan enormemente a conseguir estos objetivos y son un aliado fundamental para entornos de mejora continua.
Y recordad siempre que, sin medir, es imposible mejorar. Hay que medir antes y después, y comparar las medidas. Eso es lo que realmente me indica si estoy mejorando o empeorando.
Aquí os dejo todo el proyecto de Maven para que podáis ver el código completo y podáis jugar con él.
Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software)
Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)
mailto:alejandropg@autentia.com
Autentia Real Business Solutions S.L. - "Soporte a Desarrollo"